﻿namespace HuwmanCode.Diagnostics.Logging

open System
open System.Runtime.Serialization

/// <summary>
/// The trace message (Serializable)
/// </summary>
[<DataContract>]
type TraceMessage() =
    let mutable timestamp = Unchecked.defaultof<DateTime>
    let mutable traceLevel = Unchecked.defaultof<TraceLevel>
    let mutable machineName = Unchecked.defaultof<string>
    let mutable executableName = Unchecked.defaultof<string>
    let mutable message = Unchecked.defaultof<string>
    let mutable exceptionDetail = Unchecked.defaultof<ExceptionDetail>

    /// <summary>
    /// Gets the timestamp.
    /// </summary>
    /// <value>The timestamp.</value>
    [<DataMember>]
    member this.Timestamp
        with get() = timestamp
        and internal set(value) = timestamp <- value

    /// <summary>
    /// Gets the trace level.
    /// </summary>
    /// <value>The level.</value>
    [<DataMember>]
    member this.Level
        with get() = traceLevel
        and internal set(value) = traceLevel <- value

    /// <summary>
    /// Gets the name of the machine.
    /// </summary>
    /// <value>The name of the machine.</value>
    [<DataMember>]
    member this.MachineName
        with get() = machineName
        and internal set(value) = machineName <- value

    /// <summary>
    /// Gets the name of the executable.
    /// </summary>
    /// <value>The name of the executable.</value>
    [<DataMember>]
    member this.ExecutableName
        with get() = executableName
        and internal set(value) = executableName <- value

    /// <summary>
    /// Gets the message.
    /// </summary>
    /// <value>The message.</value>
    [<DataMember>]
    member this.Message
        with get() = message
        and internal set(value) = message <- value

    /// <summary>
    /// Gets the exception detail.
    /// </summary>
    /// <value>The exception detail.</value>
    [<DataMember>]
    member this.ExceptionDetail
        with get() = exceptionDetail
        and internal set(value) = exceptionDetail <- value

    override this.ToString() =
        let data = [|this.Level.ToString();this.Timestamp.ToLongTimeString();this.Message|]
        String.Join(" | ", data) + if not(this.ExceptionDetail = Unchecked.defaultof<ExceptionDetail>) then " \r\n" + this.ExceptionDetail.ToString() else String.Empty

/// <summary>
/// The exception details of a trace message
/// </summary>

and [<DataContract>] 
    [<AllowNullLiteral>]
    ExceptionDetail() =
    let mutable name = Unchecked.defaultof<string>
    let mutable assemblyName = Unchecked.defaultof<string>
    let mutable typeName = Unchecked.defaultof<string>
    let mutable methodName = Unchecked.defaultof<string>
    let mutable fileName = Unchecked.defaultof<string>
    let mutable lineNumber = Unchecked.defaultof<int>
    let mutable message = Unchecked.defaultof<string>
    let mutable innerException = Unchecked.defaultof<ExceptionDetail>

    /// <summary>
    /// Gets the name.
    /// </summary>
    /// <value>The name.</value>
    [<DataMember>]
    member this.Name
        with get() = name
        and internal set(value) = name <- value

    /// <summary>
    /// Gets the name of the assembly.
    /// </summary>
    /// <value>The name of the assembly.</value>
    [<DataMember>]
    member this.AssemblyName
        with get() = assemblyName
        and internal set(value) = assemblyName <- value

    /// <summary>
    /// Gets the name of the type.
    /// </summary>
    /// <value>The name of the type.</value>
    [<DataMember>]
    member this.TypeName
        with get() = typeName
        and internal set(value) = typeName <- value

    /// <summary>
    /// Gets the name of the method.
    /// </summary>
    /// <value>The name of the method.</value>
    [<DataMember>]
    member this.MethodName
        with get() = methodName
        and internal set(value) = methodName <- value

    /// <summary>
    /// Gets the name of the file.
    /// </summary>
    /// <value>The name of the file.</value>
    [<DataMember>]
    member this.FileName
        with get() = fileName
        and internal set(value) = fileName <- value

    /// <summary>
    /// Gets the line number.
    /// </summary>
    /// <value>The line number.</value>
    [<DataMember>]
    member this.LineNumber
        with get() = lineNumber
        and internal set(value) = lineNumber <- value

    /// <summary>
    /// Gets the message.
    /// </summary>
    /// <value>The message.</value>
    [<DataMember>]
    member this.Message
        with get() = message
        and internal set(value) = message <- value

    /// <summary>
    /// Gets the inner exception details.
    /// </summary>
    /// <value>The inner exception details.</value>
    [<DataMember>]
    member this.InnerException
        with get() = innerException
        and internal set(value) = innerException <- value

    override this.ToString() =
        let getData (e:ExceptionDetail) = [|"Exception " + e.Name;e.AssemblyName;e.TypeName; e.MethodName;e.Message|]
        let getInnerData (e:ExceptionDetail) = [|"Inner Exception " + e.Name;e.AssemblyName;e.TypeName; e.MethodName;e.Message|]
        let rec getInners (e:ExceptionDetail) = 
            match e with
            | null -> []
            | e -> String.Join(" | ", getInnerData e) :: getInners e.InnerException

        String.Join(" | ", getData this) + 
        String.Join("\r\n\t", getInners this.InnerException |> List.toArray)